package utils

import (
	"fmt"
	"sync/atomic"
	"time"

	"gin-gateway/core/init"
	"github.com/gomodule/redigo/redis"
)

// redis 流量统计服务
type RedisFlowCountService struct {
	AppID         string        //应用ID
	Interval      time.Duration //采集频率
	QPS           int64         //QPS
	LastTimeStamp int64         //上次unix时间戳
	TickerCount   int64         //当前流量 定时时间内流量数
	TotalCount    int64
}

// appID 应用id, interval采集频率
func NewRedisFlowCountService(appID string, interval time.Duration) *RedisFlowCountService {
	reqCounter := &RedisFlowCountService{
		AppID:         appID,
		Interval:      interval,
		QPS:           0,
		LastTimeStamp: 0,
	}
	go func() {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println(err)
			}
		}()
		ticker := time.NewTicker(interval) //定时间隔器
		for range ticker.C {
			tickerCount := atomic.SwapInt64(&reqCounter.TickerCount, 0) // 把本地的 TickerCount 清零的同时拿到清零前的值，

			currentTime := time.Now()
			dayKey := reqCounter.GetDayKey(currentTime)
			hourKey := reqCounter.GetHourKey(currentTime)
			// 把 TickerCount 加到 redis 中作为增量
			if err := RedisPipline(func(Conn redis.Conn) {
				Conn.Send("INCRBY", dayKey, tickerCount) // INCRBY 命令
				Conn.Send("EXPIRE", dayKey, 86400*2)
				Conn.Send("INCRBY", hourKey, tickerCount)
				Conn.Send("EXPIRE", hourKey, 86400*2)
			}); err != nil {
				fmt.Println("RedisConfPipeline Send err", err)
				continue
			}

			// 从 redis 获取最新的总计数
			DayTotalCount, err := reqCounter.GetDayDataByRedis(currentTime)
			if err != nil {
				fmt.Println("reqCounter.GetDayDataByRedis err", err)
				continue
			}

			nowTimeStamp := currentTime.Unix()
			if reqCounter.LastTimeStamp == 0 {
				reqCounter.LastTimeStamp = nowTimeStamp
				continue
			}

			// 用最新的总计数减去本地存的总计数，得到增量
			tickerCount = DayTotalCount - reqCounter.TotalCount
			if nowTimeStamp > reqCounter.LastTimeStamp { // 刷新qps、TotalCount、LastTimeStamp
				// 本地总计数更新成最新总计数
				atomic.AddInt64(&reqCounter.TotalCount, DayTotalCount)
				atomic.StoreInt64(&reqCounter.QPS, tickerCount/(nowTimeStamp-reqCounter.LastTimeStamp))
				reqCounter.LastTimeStamp = nowTimeStamp
			}
		}
	}()
	return reqCounter
}

func NewRedisFlowCountService1(appID string, interval time.Duration) *RedisFlowCountService {
	reqCounter := &RedisFlowCountService{
		AppID:         appID,
		Interval:      interval,
		QPS:           0,
		LastTimeStamp: 0,
	}
	go func() {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println(err)
			}
		}()
		ticker := time.NewTicker(interval) //定时间隔器
		for {
			<-ticker.C
			// 把本地的 TickerCount 清零的同时拿到清零前的值，
			tickerCount := atomic.SwapInt64(&reqCounter.TickerCount, 0)

			currentTime := time.Now()
			dayKey := reqCounter.GetDayKey(currentTime)
			hourKey := reqCounter.GetHourKey(currentTime)
			// 把 TickerCount 加到 redis 中作为增量
			if err := RedisPipline(func(c redis.Conn) {
				c.Send("INCRBY", dayKey, tickerCount)
				c.Send("EXPIRE", dayKey, 86400*2)
				c.Send("INCRBY", hourKey, tickerCount)
				c.Send("EXPIRE", hourKey, 86400*2)
			}); err != nil {
				fmt.Println("RedisPipline err", err)
				continue
			}

			// 从 redis 获取最新的总计数
			totalCount, err := reqCounter.GetDayDataByRedis(currentTime)
			if err != nil {
				fmt.Println("reqCounter.GetDayDataByRedis err", err)
				continue
			}
			nowTimeStamp := currentTime.Unix()
			if reqCounter.LastTimeStamp == 0 {
				reqCounter.LastTimeStamp = nowTimeStamp
				continue
			}

			// 用最新的总计数减去本地存的总计数，得到增量
			tickerCount = totalCount - reqCounter.TotalCount
			if nowTimeStamp > reqCounter.LastTimeStamp { // 刷新qps、TotalCount、LastTimeStamp

				// 本地总计数更新成最新总计数
				reqCounter.TotalCount = totalCount
				reqCounter.QPS = tickerCount / (nowTimeStamp - reqCounter.LastTimeStamp)
				reqCounter.LastTimeStamp = nowTimeStamp
			}
		}
	}()
	return reqCounter
}

func (r *RedisFlowCountService) GetDayKey(t time.Time) string {
	return fmt.Sprintf("%s_%s_%s", RedisFlowDayKey, t.In(Init.TimeLocation).Format("20060102"), r.AppID)
}

func (r *RedisFlowCountService) GetHourKey(t time.Time) string {
	return fmt.Sprintf("%s_%s_%s", RedisFlowHourKey, t.In(Init.TimeLocation).Format("2006010215"), r.AppID)
}

func (r *RedisFlowCountService) GetHourDataByRedis(t time.Time) (int64, error) {
	return redis.Int64(RedisDo("GET", r.GetHourKey(t)))
}

func (r *RedisFlowCountService) GetDayDataByRedis(t time.Time) (int64, error) {
	return redis.Int64(RedisDo("GET", r.GetDayKey(t)))
}

// Increase 原子增加
func (r *RedisFlowCountService) Increase() {
	go func() {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println(err)
			}
		}()
		atomic.AddInt64(&r.TickerCount, 1)
	}()
}
